// Package daemon 守护进程模式
package daemon

import (
    "context"
    "crypto/x509"
    "encoding/pem"
    "fmt"
    "log/slog"
    "os"
    "path/filepath"
    "strings"
    "time"

    "github.com/cnssl/cert-deploy-iis/internal/config"
    "github.com/cnssl/cert-deploy-iis/internal/csr"
    "github.com/cnssl/cert-deploy-iis/internal/deployer"
    "github.com/cnssl/cert-deploy-iis/internal/fetcher"
    // "github.com/cnssl/cert-deploy-iis/internal/scanner"
    "github.com/cnssl/cert-deploy-iis/internal/validator"
)

// Daemon 守护进程
type Daemon struct {
	cfgManager *config.Manager
	logger     *slog.Logger
	stopCh     chan struct{}
}

// New 创建守护进程
func New(cfgManager *config.Manager, logger *slog.Logger) *Daemon {
	return &Daemon{
		cfgManager: cfgManager,
		logger:     logger,
		stopCh:     make(chan struct{}),
	}
}

// Run 运行守护进程
func (d *Daemon) Run() error {
	d.logger.Info("daemon started")

	ticker := time.NewTicker(10 * time.Minute) // 每10分钟检查一次
	defer ticker.Stop()

	// 立即执行一次检查
	if err := d.checkAndDeploy(); err != nil {
		d.logger.Error("initial check failed", "error", err)
	}

	for {
		select {
		case <-ticker.C:
			if err := d.checkAndDeploy(); err != nil {
				d.logger.Error("check failed", "error", err)
			}

		case <-d.stopCh:
			d.logger.Info("daemon stopped")
			return nil
		}
	}
}

// Stop 停止守护进程
func (d *Daemon) Stop() {
	close(d.stopCh)
}

// checkAndDeploy 检查并部署证书
func (d *Daemon) checkAndDeploy() error {
	d.logger.Info("checking sites")

	// 加载所有站点配置
	sites, err := d.cfgManager.ListSites()
	if err != nil {
		return fmt.Errorf("failed to list sites: %w", err)
	}

	for _, site := range sites {
		if !site.Enabled {
			continue
		}

		// 检查是否需要处理
		if d.shouldProcess(site) {
			d.logger.Info("processing site", "site", site.SiteName)

			if err := d.processSite(site); err != nil {
				d.logger.Error("failed to process site",
					"site", site.SiteName,
					"error", err)
				continue
			}
		}
	}

	return nil
}

// shouldProcess 判断是否应该处理该站点
func (d *Daemon) shouldProcess(site *config.SiteConfig) bool {
	now := time.Now()

	// 1. 检查是否到了重试时间
	if !site.Metadata.NextRetryAt.IsZero() && now.After(site.Metadata.NextRetryAt) {
		return true
	}

	// 2. 检查是否需要续期
	if site.NeedsRenewal() {
		// 检查上次检查时间,避免频繁检查
		if site.Metadata.LastCheckAt.IsZero() ||
			now.Sub(site.Metadata.LastCheckAt) > time.Duration(site.Schedule.CheckIntervalHours)*time.Hour {
			return true
		}
	}

	return false
}

// processSite 处理单个站点
func (d *Daemon) processSite(site *config.SiteConfig) error {
    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()

    f := fetcher.New(30 * time.Second)
    info, err := f.Info(ctx, site.API.URL, site.API.ReferID)
    if err != nil {
        d.logger.Error("failed to GET cert info", "site", site.SiteName, "error", err)
        site.Metadata.LastCheckAt = time.Now()
        site.Metadata.NextRetryAt = d.calculateNextRetry("error")
        return d.cfgManager.SaveSite(site)
    }

    // 文件验证（IIS 使用物理路径）
    if info.File != nil && strings.EqualFold(site.Validation.Method, "file") && site.IIS != nil && site.IIS.PhysicalPath != "" {
        target := filepath.Join(site.IIS.PhysicalPath, info.File.Path)
        if err := os.MkdirAll(filepath.Dir(target), 0755); err == nil {
            if werr := os.WriteFile(target, []byte(info.File.Content), 0644); werr != nil {
                d.logger.Warn("write file challenge failed", "site", site.SiteName, "error", werr)
            } else {
                d.logger.Info("file challenge written", "path", target)
            }
        }
    }

    site.Metadata.LastCheckAt = time.Now()

    if strings.EqualFold(info.Status, "active") && info.Cert != "" {
        // 直接部署（若更“新”）
        newCert, perr := parseCertificateFromPEM(info.Cert)
        if perr != nil {
            d.logger.Error("parse new certificate failed", "error", perr)
            site.Metadata.NextRetryAt = d.calculateNextRetry("error")
            return d.cfgManager.SaveSite(site)
        }
        currentExpiry := site.Metadata.CertExpiresAt
        const minImproveDays = 15
        if currentExpiry.IsZero() || newCert.NotAfter.Sub(currentExpiry) >= (minImproveDays*24*time.Hour) {
            // 校验覆盖
            dv := validator.NewDomainValidator(site.Domains, site.Validation.IgnoreDomainMismatch)
            if err := dv.ValidateDomainCoverage(newCert); err != nil {
                d.logger.Error("domain validation failed", "error", err)
                site.Metadata.NextRetryAt = d.calculateNextRetry("error")
                return d.cfgManager.SaveSite(site)
            }
            // 本地 key
            keyBytes, kerr := os.ReadFile(site.Paths.PrivateKey)
            if kerr != nil { d.logger.Error("read local key failed", "error", kerr); site.Metadata.NextRetryAt = d.calculateNextRetry("error"); return d.cfgManager.SaveSite(site) }
            v := validator.New("")
            if err := v.ValidateKey(string(keyBytes)); err != nil { d.logger.Error("key invalid", "error", err); site.Metadata.NextRetryAt = d.calculateNextRetry("error"); return d.cfgManager.SaveSite(site) }
            if info.IntermediateCert != "" { if err := v.ValidateCA(info.IntermediateCert); err != nil { d.logger.Error("CA invalid", "error", err); site.Metadata.NextRetryAt = d.calculateNextRetry("error"); return d.cfgManager.SaveSite(site) } }

            iis := deployer.NewIISDeployer(site.IIS.SiteName, site.IIS.Hostname, site.IIS.Port, d.cfgManager.GetWorkDir())
            if err := iis.Deploy(info.Cert, info.IntermediateCert, string(keyBytes)); err != nil {
                d.logger.Error("deployment failed", "error", err)
                site.Metadata.NextRetryAt = d.calculateNextRetry("error")
                return d.cfgManager.SaveSite(site)
            }
            site.Metadata.CertExpiresAt = newCert.NotAfter
            site.Metadata.CertSerial = fmt.Sprintf("%X", newCert.SerialNumber)
            site.Metadata.LastDeployAt = time.Now()
            site.Metadata.LastCheckAt = time.Now()
            site.Metadata.NextRetryAt = time.Time{}
            d.logger.Info("certificate deployed by daemon", "site", site.SiteName)
            return d.cfgManager.SaveSite(site)
        }
        return d.cfgManager.SaveSite(site)
    }

    

    // 需要发起更新：POST（生成本地 CSR/Key）
    commonName := info.CommonName
    if commonName == "" { if len(site.Domains) > 0 { commonName = site.Domains[0] } else { commonName = site.SiteName } }
    keyPEM, csrPEM, _, gerr := csr.GenerateKeyAndCSR(
        csr.KeyOptions{Type: site.Key.Type, Size: site.Key.Size, Curve: site.Key.Curve},
        csr.CSROptions{CommonName: commonName, Organization: site.CSR.Organization, Country: site.CSR.Country, State: site.CSR.State, Locality: site.CSR.Locality, Email: site.CSR.Email},
    )
    if gerr == nil {
        if err := os.MkdirAll(filepath.Dir(site.Paths.PrivateKey), 0755); err == nil {
            _ = os.WriteFile(site.Paths.PrivateKey, []byte(keyPEM), 0600)
        }
    }
    post, err := f.StartOrUpdate(ctx, site.API.URL, site.API.ReferID, csrPEM, commonName, site.Validation.Method)
    if err != nil {
        d.logger.Error("POST update failed", "error", err)
        site.Metadata.NextRetryAt = d.calculateNextRetry("error")
        return d.cfgManager.SaveSite(site)
    }
    site.Metadata.LastIssueState = post.Status
    switch strings.ToLower(post.Status) {
    case "unpaid":
        site.Metadata.IssueRetryCount++
        site.Metadata.NextRetryAt = time.Now().Add(6 * time.Hour)
    case "pending", "processing", "approving":
        site.Metadata.NextRetryAt = time.Now().Add(30 * time.Minute)
    case "active":
        // 立即 GET + 部署（已在前面处理 active 分支）
        site.Metadata.NextRetryAt = time.Now().Add(24 * time.Hour)
        site.Metadata.IssueRetryCount++
    default:
        site.Metadata.NextRetryAt = d.calculateNextRetry(post.Status)
    }
    return d.cfgManager.SaveSite(site)
}

// calculateNextRetry 计算下次重试时间
func (d *Daemon) calculateNextRetry(status string) time.Time {
	now := time.Now()

	// 快速重试状态
	fastRetryStates := map[string]bool{
		"unpaid":     true,
		"pending":    true,
		"processing": true,
		"approving":  true,
	}

	if fastRetryStates[status] {
		// 30分钟后重试
		return now.Add(30 * time.Minute)
	}

	// 其他状态:6小时后重试
	return now.Add(6 * time.Hour)
}

// CalculateNextCheck 计算下次检查时间(智能调度)
func CalculateNextCheck(sites []*config.SiteConfig) time.Duration {
	minDays := 999

	for _, site := range sites {
		if !site.Enabled {
			continue
		}

		days := site.DaysUntilExpiry()
		if days < minDays {
			minDays = days
		}
	}

	// 根据最近的到期时间调整检查间隔
	// > 30 天:12 小时
	// 15-30 天:6 小时
	// 7-15 天:2 小时
	// < 7 天:30 分钟

	if minDays > 30 {
		return 12 * time.Hour
	} else if minDays > 15 {
		return 6 * time.Hour
	} else if minDays > 7 {
		return 2 * time.Hour
	} else {
		return 30 * time.Minute
	}
}

// parseCertificateFromPEM 解析 PEM 字符串为 X.509 证书
func parseCertificateFromPEM(pemStr string) (*x509.Certificate, error) {
    block, _ := pem.Decode([]byte(pemStr))
    if block == nil || block.Type != "CERTIFICATE" {
        return nil, fmt.Errorf("invalid certificate PEM")
    }
    cert, err := x509.ParseCertificate(block.Bytes)
    if err != nil {
        return nil, err
    }
    return cert, nil
}
